use std::time::{Duration, Instant};

use crate::{nacos::BASE_URL, LOGIN_TOKEN};
use anyhow::Result;
// use hyper::{client::ResponseFuture, Body, Client, Method, Request, Response};

use async_recursion::async_recursion;
use crossbeam::select;
use crossbeam_channel::{tick, unbounded};
use serde::{Deserialize, Serialize};

#[derive(Debug, Deserialize, Serialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct Login {
    pub username: String,
    pub password: String,
}

///!
/// {"accessToken":"eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJuYWNvcyIsImV4cCI6MTYwNTYyOTE2Nn0.2TogGhhr11_vLEjqKko1HJHUJEmsPuCxkur-CfNojDo","tokenTtl":18000,"globalAdmin":true}
///!
#[derive(Debug, Deserialize, Serialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct LoginRes {
    pub access_token: String,
    pub token_ttl: u64,
    pub global_admin: bool,
    pub username: String,
}

impl Default for Login {
    fn default() -> Self {
        Self {
            username: "nacos".to_string(),
            password: "nacos".to_string(),
        }
    }
}
///!
/// curl -X POST '127.0.0.1:8848/nacos/v1/auth/login' -d 'username=nacos&password=nacos'
///!
///
///
// #[async_recursion]
pub async fn get_login_token(login: &Login) -> Result<LoginRes> {
    println!("Getting {:?}", &login);
    let url = format!(
        "{}/nacos/v1/auth/login?username={}&password={}",
        BASE_URL.as_str(),
        login.username,
        login.password
    );
    println!("url {:?}", &url);
    let res = reqwest::Client::default()
        .post(&url)
        .send()
        .await?
        .json::<LoginRes>()
        .await?;
    println!("res {:?}", &res);

    start_token_refresh(login.clone(), &res);

    Ok(res)
}

#[async_recursion(?Send)]
async fn start_token_refresh(login: Login, login_res: &LoginRes) {
    if login_res.token_ttl > 0 {
        refresh_token(login_res).await;
        let ttl = login_res.token_ttl;
        print!("{:?}", login);
        let start = Instant::now();
        let ticket = tick(Duration::from_secs(ttl));

        let (_s, r) = unbounded::<()>();

        loop {
            select! {
                recv(r)->msg=>{
                    println!("recve {:?}",msg);
                }
                recv(ticket)->msg=>{
                    println!("elapsed: {:?} {:?}", msg.unwrap(),start.elapsed());
                    let res =   get_login_token(&login).await.unwrap();
                    refresh_token(&res).await;
                }
            }
        }
    }
}

async fn refresh_token(login_res: &LoginRes) -> u64 {
    let mut token = LOGIN_TOKEN.lock().await;
    token.insert("token".to_string(), login_res.clone());
    login_res.token_ttl
}

#[cfg(test)]
mod test {

    use super::*;

    #[tokio::test]
    async fn test_login_token() {
        let login = Login::default();

        let res = get_login_token(&login).await;

        assert!(res.is_ok());
        match res {
            Ok(value) => println!("{:?}", value),
            Err(e) => print!("{}", e),
        };
    }

    #[tokio::test]
    async fn test_time_login_token() {
        let start = Instant::now();

        let login = Login::default();
        let login_res = get_login_token(&login).await.unwrap();

        let _ttl = refresh_token(&login_res).await;
        let ticket = tick(Duration::from_secs(1));

        let (_s, r) = unbounded::<()>();

        loop {
            select! {
                recv(r)->msg=>{
                    println!("recve {:?}",msg);
                }
                recv(ticket)->msg=>{
                    println!("elapsed: {:?} {:?}", msg.unwrap(),start.elapsed());

                    let login_res = get_login_token(&login).await.unwrap();
                    refresh_token(&login_res).await;

                }
            }
        }
    }
}
